//
// Author: Vladimir Migashko <migashko@gmail.com>, (C) 2011
//
// Copyright: See COPYING file that comes with this distribution
//

#ifndef FASYS_CHANNELS_CHANNELS_CONTAINER_HPP
#define FASYS_CHANNELS_CHANNELS_CONTAINER_HPP

#include "types.hpp"
#include "tags.hpp"
#include "channel_info.hpp"

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/composite_key.hpp>

namespace fasys{ namespace channels{

using namespace boost;
using namespace boost::multi_index;

struct f_channel_update
{
  time_t death_time;
  time_t update_time;
  message_id_t message_id_counter;
  time_t subscribe_time;
  size_t min_size;
  size_t max_size;


  f_channel_update(const channel_info& ci)
    : death_time(ci.death_time)
    , update_time(ci.update_time)
    , message_id_counter(ci.message_id_counter)
    , subscribe_time(ci.subscribe_time)
    , min_size(ci.min_size)
    , max_size(ci.max_size)
  {
  }

  void operator()(channel_info& ci)
  {
    ci.death_time = death_time;
    ci.update_time = update_time;
    ci.message_id_counter = message_id_counter;
    ci.subscribe_time = subscribe_time;
    ci.min_size = min_size;
    ci.max_size = max_size;
    if ( ci.create_time == 0 )
      ci.create_time = ci.update_time;
  };
};

struct channel_ordered_by_name
  : ordered_unique<
      tag<_name_>,
      composite_key<
        channel_info,
        member<channel_info, std::string, &channel_info::name>
      >
   >
{
};

struct channel_ordered_by_channel_id
  : ordered_unique<
      tag<_channel_id_>,
      composite_key<
        channel_info,
        member<channel_info, channel_id_t, &channel_info::channel_id>
      >
   >
{
};

/// //////////////////////
/// //////////////////////////////////////////////////


struct channel_indices
  : indexed_by<
      channel_ordered_by_name,
      channel_ordered_by_channel_id
    >
{};

typedef multi_index_container<channel_info, channel_indices> channel_multi_index;

class channels_container
  : private channel_multi_index
{
  typedef channel_multi_index super;
  typedef super::index<_name_>::type by_name_type;
  typedef super::index<_channel_id_>::type by_channel_id_type;

public:
  channels_container()
    : _channel_id_counter(0)
    , _inactive_timeout(FASYS_INACTIVE_CHANNEL_TIMEOUT)
  {
  }

  channel_id_t first_channel_id() const
  {
    if ( super::empty() ) return 0;
    return super::begin()->channel_id;
  };

  channel_id_t next_channel_id(channel_id_t channel_id) const
  {
    if ( super::empty() ) 
      return 0;

    const by_channel_id_type& by_channel_id = super::get<_channel_id_>();
    by_channel_id_type::const_iterator itr = by_channel_id.find(channel_id);
    if ( itr == by_channel_id.end() )
      return 0;

    ++itr;
    if ( itr == by_channel_id.end() )
      return 0;

    return itr->channel_id;
  };


  channel_info create(const std::string& name)
  {
    by_name_type& by_name = super::get<_name_>();
    by_name_type::iterator itr = by_name.find(name);
    if ( itr != by_name.end() )
      return *itr;

    // Пока не открываем канал
    channel_info channel;
    channel.channel_id = ++_channel_id_counter;
    channel.name = name;
    std::pair<super::iterator, bool> result = super::insert(channel);

    if ( !result.second )
        throw;

    return channel;
  }
  
  channel_info find(const std::string& name)
  {
    by_name_type& by_name = super::get<_name_>();
    by_name_type::iterator itr = by_name.find(name);
    if ( itr != by_name.end() )
      return *itr;
    channel_info channel;
    channel.channel_id = 0;
    return channel;
  }

  /// @return true - обновлен, false- канал не найден
  bool update( const channel_info& c  )
  {
    by_channel_id_type& by_channel_id = super::get<_channel_id_>();
    by_channel_id_type::iterator itr = by_channel_id.find(c.channel_id);
    if ( itr == by_channel_id.end() )
    {
      /*std::pair<super::iterator, bool> result
          = super::insert(c);
      if ( !result.second )
        throw;
      return true;
      */
      //return false;
      throw;
    }

    by_channel_id.modify(itr, f_channel_update(c) );
    return true;
  }

  /// Поготавливает сообщение и создает канал если необходимо
  /*void prepare(message_info& mi)
  {
    by_name_type& by_name = super::get<_name_>();
    by_name_type::iterator itr = by_name.lower_bound(mi.channel_name);
    if ( itr != by_name.end() && itr-> )
  }*/

private:


private:

  channel_id_t _channel_id_counter;
  time_t _inactive_timeout;

};



}}

#endif

